﻿using System;
using ch10.binary;

namespace ch10.interpreter
{
    using byteOrder = LittleEndian;

    public static partial class Instr
    {
        private static void MemorySize( Vm vm, object args)
        {
            vm.OperandStack.PushU32(vm.Memory.Size());
        }

        private static void MemoryGrow( Vm vm, object args)
        {
            var oldSize = vm.Memory.Grow(vm.OperandStack.PopU32());
            vm.OperandStack.PushU32(oldSize);
        }

        private static ulong GetOffset( Vm vm, object memArg)
        {
            var offset = ((MemArg) memArg).Offset;
            return (ulong) vm.OperandStack.PopU32() + offset;
        }

        private static byte ReadU8( Vm vm, object memArg)
        {
            var buf = new byte[1];
            var offset = GetOffset( vm, memArg);
            vm.Memory.Read(offset, buf);
            return buf[0];
        }

        private static ushort ReadU16( Vm vm, object memArg)
        {
            var buf = new byte[2];
            var offset = GetOffset( vm, memArg);
            vm.Memory.Read(offset, buf);
            return byteOrder.Uint16(ref buf);
        }

        private static uint ReadU32( Vm vm, object memArg)
        {
            var buf = new byte [4];
            var offset = GetOffset( vm, memArg);
            vm.Memory.Read(offset, buf);
            return byteOrder.Uint32(ref buf);
        }

        private static ulong ReadU64( Vm vm, object memArg)
        {
            var buf = new byte[8];
            var offset = GetOffset( vm, memArg);
            vm.Memory.Read(offset, buf);
            return byteOrder.Uint64( ref buf);
        }

        // load
        private static void I32Load( Vm vm, object memArg)
        {
            var val = ReadU32( vm, memArg);
            vm.OperandStack.PushU32(val);
        }

        private static void I64Load( Vm vm, object memArg)
        {
            var val = ReadU64( vm, memArg);
            vm.PushU64(val);
        }

        private static void F32Load( Vm vm, object memArg)
        {
            var val = ReadU32( vm, memArg);
            vm.PushU32(val);
        }

        private static void F64Load( Vm vm, object memArg)
        {
            var val = ReadU64( vm, memArg);
            vm.PushU64(val);
        }

        private static void I32Load8S( Vm vm, object memArg)
        {
            var val = ReadU8( vm, memArg);
            vm.PushS32((sbyte) val);
        }

        private static void I32Load8U( Vm vm, object memArg)
        {
            var val = ReadU8( vm, memArg);
            vm.OperandStack.PushU32(val);
        }

        private static void I32Load16S( Vm vm, object memArg)
        {
            var val = ReadU16( vm, memArg);
            vm.PushS32((short) (val));
        }

        private static void I32Load16U( Vm vm, object memArg)
        {
            var val = ReadU16( vm, memArg);
            vm.PushU32(val);
        }

        private static void I64Load8S( Vm vm, object memArg)
        {
            var val = ReadU8( vm, memArg);
            vm.PushS64((sbyte) (val));
        }

        private static void I64Load8U( Vm vm, object memArg)
        {
            var val = ReadU8( vm, memArg);
            vm.PushU64(val);
        }

        private static void I64Load16S( Vm vm, object memArg)
        {
            var val = ReadU16( vm, memArg);
            vm.PushS64((short) (val));
        }

        private static void I64Load16U( Vm vm, object memArg)
        {
            var val = ReadU16( vm, memArg);
            vm.PushU64(val);
        }

        private static void I64Load32S( Vm vm, object memArg)
        {
            var val = ReadU32( vm, memArg);
            vm.PushS64((int) (val));
        }

        private static void I64Load32U( Vm vm, object memArg)
        {
            var val = ReadU32( vm, memArg);
            vm.OperandStack.PushU64(val);
        }


        private static void WriteU8( Vm vm, object memArg, byte n)
        {
            var buf = new byte[1];
            buf[0] = n;
            var offset = GetOffset( vm, memArg);
            vm.Memory.Write(offset, buf);
        }

        private static void WriteU16( Vm vm, object memArg, UInt16 n)
        {
            var buf = new byte[2];
            byteOrder.PutUint16( ref buf, n);
            var offset = GetOffset( vm, memArg);
            vm.Memory.Write(offset, buf);
        }

        private static void WriteU32( Vm vm, object memArg, uint n)
        {
            var buf = new byte[4];
            byteOrder.PutUint32(ref buf, n);
            var offset = GetOffset( vm, memArg);
            vm.Memory.Write(offset, buf);
        }

        private static void WriteU64( Vm vm, object memArg, ulong n)
        {
            var buf = new byte[8];
            byteOrder.PutUint64(ref buf, n);
            var offset = GetOffset( vm, memArg);
            vm.Memory.Write(offset, buf);
        }

        // store
        private static void I32Store( Vm vm, object memArg)
        {
            var val = vm.OperandStack.PopU32();
            WriteU32( vm, memArg, val);
        }

        private static void I64Store( Vm vm, object memArg)
        {
            var val = vm.OperandStack.PopU64();
            WriteU64( vm, memArg, val);
        }

        private static void F32Store( Vm vm, object memArg)
        {
            var val = vm.OperandStack.PopU32();
            WriteU32( vm, memArg, val);
        }

        private static void F64Store( Vm vm, object memArg)
        {
            var val = vm.OperandStack.PopU64();
            WriteU64( vm, memArg, val);
        }

        private static void I32Store8( Vm vm, object memArg)
        {
            var val = vm.OperandStack.PopU32();
            WriteU8( vm, memArg, (byte) (val));
        }

        private static void I32Store16( Vm vm, object memArg)
        {
            var val = vm.OperandStack.PopU32();
            WriteU16( vm, memArg, (UInt16) (val));
        }

        private static void I64Store8( Vm vm, object memArg)
        {
            var val = vm.OperandStack.PopU64();
            WriteU8( vm, memArg, (byte) (val));
        }

        private static void I64Store16( Vm vm, object memArg)
        {
            var val = vm.OperandStack.PopU64();
            WriteU16( vm, memArg, (UInt16) (val));
        }

        private static void I64Store32( Vm vm, object memArg)
        {
            var val = vm.OperandStack.PopU64();
            WriteU32( vm, memArg, (uint) (val));
        }
    }
}